package cn.iocoder.yudao.module.javainfo.job.domain;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig;
import cn.iocoder.yudao.module.infra.service.file.FileConfigService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 域名IP同步 Job
 *
 * 获取指定域名对应的IP地址，并更新文件配置表中的endpoint字段
 *
 */
@Component
@Slf4j
public class DomainIpSyncJob implements JobHandler {

    /**
     * 目标域名
     */
    private static final String TARGET_DOMAIN = "mi.zhangjt.cn";

    /**
     * 目标文件配置ID
     */
    private static final Long TARGET_FILE_CONFIG_ID = 28L;

    @Resource
    private FileConfigService fileConfigService;

    @Override
    public String execute(String param) throws Exception {
        // 1. 获取域名对应的IP地址
        String ipAddress = getIpAddressByDomain(TARGET_DOMAIN);
        if (StrUtil.isBlank(ipAddress)) {
            log.error("[execute][获取域名({})对应的IP地址失败]", TARGET_DOMAIN);
            return StrUtil.format("获取域名({})对应的IP地址失败", TARGET_DOMAIN);
        }
        log.info("[execute][获取域名({})对应的IP地址为({})]", TARGET_DOMAIN, ipAddress);

        // 2. 获取文件配置
        FileConfigDO fileConfig = fileConfigService.getFileConfig(TARGET_FILE_CONFIG_ID);
        if (fileConfig == null) {
            log.error("[execute][文件配置({})不存在]", TARGET_FILE_CONFIG_ID);
            return StrUtil.format("文件配置({})不存在", TARGET_FILE_CONFIG_ID);
        }

        // 3. 解析配置，获取endpoint字段
        String configStr = JSONUtil.toJsonStr(fileConfig.getConfig());
        S3FileClientConfig configJson = JSON.parseObject(configStr, S3FileClientConfig.class);
        String oldEndpoint = configJson.getEndpoint();
        if (StrUtil.isBlank(oldEndpoint)) {
            log.error("[execute][文件配置({})的endpoint字段为空]", TARGET_FILE_CONFIG_ID);
            return StrUtil.format("文件配置({})的endpoint字段为空", TARGET_FILE_CONFIG_ID);
        }

        // 4. 替换IP地址
        String newEndpoint = replaceIpAddress(oldEndpoint, TARGET_DOMAIN, ipAddress);

        // 5. 如果endpoint没有变化，则不更新
        if (oldEndpoint.equals(newEndpoint)) {
            log.info("[execute][文件配置({})的endpoint字段未发生变化，无需更新]", TARGET_FILE_CONFIG_ID);
            return StrUtil.format("文件配置({})的endpoint字段未发生变化，无需更新", TARGET_FILE_CONFIG_ID);
        }

        // 6. 更新配置
        configJson.setEndpoint(newEndpoint);
        fileConfig.setConfig(configJson);
        fileConfigService.updateFileConfig(convertToFileConfigSaveReqVO(fileConfig));

        log.info("[execute][更新文件配置({})的endpoint字段成功，旧值({})，新值({})]", TARGET_FILE_CONFIG_ID, oldEndpoint, newEndpoint);
        return StrUtil.format("更新文件配置({})的endpoint字段成功，旧值({})，新值({})", TARGET_FILE_CONFIG_ID, oldEndpoint, newEndpoint);
    }

    /**
     * 替换URL中的旧IP地址为新IP地址，保留端口号和域名
     *
     * @param url 原始URL
     * @param domain 域名
     * @param newIpAddress 新的IP地址
     * @return 替换后的URL
     */
    private String replaceIpAddress(String url, String domain, String newIpAddress) {
        try {
            // 判断是否是http或https协议
            if (url.startsWith("http://") || url.startsWith("https://")) {
                String protocol = url.startsWith("https://") ? "https://" : "http://";
                String remaining = url.substring(protocol.length());

                // 分离主机部分和路径部分
                String[] parts = remaining.split("/", 2);
                String hostPart = parts[0];
                String pathPart = parts.length > 1 ? "/" + parts[1] : "";

                // 分离主机和端口
                String host;
                String port = "";
                if (hostPart.contains(":")) {
                    String[] hostParts = hostPart.split(":", 2);
                    host = hostParts[0];
                    port = ":" + hostParts[1];
                } else {
                    host = hostPart;
                }

                // 检查主机是否是IP地址格式
                if (isIpAddress(host)) {
                    // 如果是IP地址，则替换为新的IP地址
                    return protocol + newIpAddress + port + pathPart;
                } else if (host.equals(domain)) {
                    // 如果是域名，则保持不变
                    return url;
                } else {
                    // 其他情况也保持不变
                    return url;
                }
            }
            return url;
        } catch (Exception e) {
            log.warn("[replaceIpAddress][URL({})IP替换异常，返回原始URL]", url, e);
            return url;
        }
    }

    /**
     * 判断字符串是否为IP地址格式
     *
     * @param str 待判断的字符串
     * @return 是否为IP地址
     */
    private boolean isIpAddress(String str) {
        if (str == null || str.isEmpty()) {
            return false;
        }
        Pattern pattern = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
        Matcher matcher = pattern.matcher(str);
        return matcher.matches();
    }

    /**
     * 获取域名对应的IP地址
     *
     * @param domain 域名
     * @return IP地址
     */
    private String getIpAddressByDomain(String domain) {
        try {
            InetAddress inetAddress = InetAddress.getByName(domain);
            return inetAddress.getHostAddress();
        } catch (Exception e) {
            log.error("[getIpAddressByDomain][获取域名({})对应的IP地址异常]", domain, e);
            return null;
        }
    }

    /**
     * 将FileConfigDO转换为FileConfigSaveReqVO
     *
     * @param fileConfig 文件配置
     * @return FileConfigSaveReqVO
     */
    private cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO convertToFileConfigSaveReqVO(FileConfigDO fileConfig) {
        cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO reqVO = new cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigSaveReqVO();
        reqVO.setId(fileConfig.getId());
        reqVO.setName(fileConfig.getName());
        reqVO.setStorage(fileConfig.getStorage());
        reqVO.setRemark(fileConfig.getRemark());
        reqVO.setConfig(JsonUtils.parseObject2(JSONUtil.toJsonStr(fileConfig.getConfig()), Map.class));
        return reqVO;
    }
}

